package cn.edu.zut.qixiang.mock;

import java.sql.*;
import java.time.LocalTime;
import java.util.Random;

/**
 * @author 86178
 */
public class WeatherHourlyDataGenerator {

    // 数据库配置
    private static final String URL = "jdbc:mysql://localhost:3306/qixiang?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8";
    private static final String USER = "root";
    private static final String PASSWORD = "520521fcf..@1234";

    // 数据生成参数
    private static final int BATCH_SIZE = 100; // 批处理大小

    public static void main(String[] args) {
        try (Connection conn = DriverManager.getConnection(URL, USER, PASSWORD)) {
            conn.setAutoCommit(false);
            Random random = new Random();

            // 查询weather_daily表中的所有记录，获取正确的daily_id和对应的district_id
            String selectDailySql = "SELECT daily_id, district_id, weather_date, weather_type, max_temperature, min_temperature " +
                    "FROM weather_daily ORDER BY daily_id";

            String insertHourlySql = "INSERT INTO weather_hourly (hourly_id, district_id, daily_id, hour_time, temperature, wind_direction," +
                    " wind_speed, precipitation, pressure, cloud_cover, visibility) " +
                    "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";

            try (PreparedStatement selectStmt = conn.prepareStatement(selectDailySql);
                 PreparedStatement insertStmt = conn.prepareStatement(insertHourlySql)) {

                ResultSet dailyRs = selectStmt.executeQuery();
                int hourlyId = 1;
                int totalRecords = 0;

                System.out.println("开始生成小时气象数据...");

                // 遍历weather_daily表中的每一天
                while (dailyRs.next()) {
                    int dailyId = dailyRs.getInt("daily_id");
                    int districtId = dailyRs.getInt("district_id");
                    Date weatherDate = dailyRs.getDate("weather_date");
                    String weatherType = dailyRs.getString("weather_type");
                    double maxTemperature = dailyRs.getDouble("max_temperature");
                    double minTemperature = dailyRs.getDouble("min_temperature");

                    System.out.println("正在生成 daily_id=" + dailyId + ", district_id=" + districtId +
                            ", 日期=" + weatherDate + " 的小时数据...");

                    // 根据天气类型判断是否为雨天
                    boolean isRainyDay = isRainyWeather(weatherType);
                    double dailyPrecipitation = isRainyDay ? random.nextDouble() * 40 + 5 : 0; // 雨天日降水量5-45mm

                    // 用于存储每小时降水量的数组
                    double[] hourlyPrecipitations = new double[24];

                    // 预计算全天的降水量分布
                    if (isRainyDay) {
                        calculateDailyPrecipitationDistribution(hourlyPrecipitations, dailyPrecipitation, random);
                    }

                    // 生成24小时记录，从1:00开始到24:00结束
                    for (int hour = 1; hour <= 24; hour++) {
                        WeatherData weatherData = generateRealisticWeather(hour, maxTemperature, minTemperature,
                                weatherType, isRainyDay, hourlyPrecipitations[hour-1], random);

                        insertStmt.setInt(1, hourlyId);
                        insertStmt.setInt(2, districtId);
                        insertStmt.setInt(3, dailyId); // 使用正确的daily_id

                        // 处理24:00时间（在Java中表示为00:00，但在数据库中存储为24:00）
                        LocalTime hourTime;
                        if (hour == 24) {
                            hourTime = LocalTime.of(0, 0); // 24:00在数据库中存储为00:00
                        } else {
                            hourTime = LocalTime.of(hour, 0);
                        }

                        insertStmt.setTime(4, java.sql.Time.valueOf(hourTime));
                        insertStmt.setDouble(5, weatherData.temperature);
                        insertStmt.setString(6, weatherData.windDirection);
                        insertStmt.setDouble(7, weatherData.windSpeed);
                        insertStmt.setDouble(8, weatherData.precipitation);
                        insertStmt.setDouble(9, weatherData.pressure);
                        insertStmt.setInt(10, weatherData.cloudCover);
                        insertStmt.setDouble(11, weatherData.visibility);

                        insertStmt.addBatch();
                        hourlyId++;
                        totalRecords++;

                        // 批量提交
                        if (hourlyId % BATCH_SIZE == 0) {
                            insertStmt.executeBatch();
                            conn.commit();
                            System.out.println("已提交 " + hourlyId + " 条记录...");
                        }
                    }

                    // 每处理100天输出一次进度
                    if (dailyId % 100 == 0) {
                        System.out.println("已完成 " + dailyId + " 天的数据生成");
                    }
                }

                // 提交剩余记录
                insertStmt.executeBatch();
                conn.commit();

                System.out.println("数据生成完成，共生成 " + totalRecords + " 条小时记录。");
                System.out.println("时间范围：每天从1:00到24:00");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 判断是否为雨天
     */
    private static boolean isRainyWeather(String weatherType) {
        return weatherType.contains("雨") || "暴雨".equals(weatherType) || "大雨".equals(weatherType) ||
                "中雨".equals(weatherType) || "小雨".equals(weatherType);
    }

    /**
     * 计算全天的降水量分布
     */
    private static void calculateDailyPrecipitationDistribution(double[] hourlyPrecipitations,
                                                                double dailyPrecipitation,
                                                                Random random) {
        // 降水时间分布权重 - 下午权重最高 (对应1-24时)
        double[] hourWeights = {
                0.01, 0.005, 0.005, 0.01, 0.02,  // 1-5点
                0.02, 0.03, 0.05, 0.07, 0.09,    // 6-10点
                0.10, 0.12, 0.15, 0.18, 0.15,    // 11-15点
                0.12, 0.10, 0.08, 0.06, 0.04,    // 16-20点
                0.03, 0.02, 0.01, 0.02           // 21-24点
        };

        double totalWeight = 0;
        for (double weight : hourWeights) {
            totalWeight += weight;
        }

        // 第一次分配：按权重基础分配
        double remainingPrecipitation = dailyPrecipitation;
        for (int hour = 0; hour < 24; hour++) {
            double baseAmount = (hourWeights[hour] / totalWeight) * dailyPrecipitation;
            // 添加随机波动
            double randomFactor = 1.2 + random.nextDouble() * 0.6; // 1.2-1.8
            hourlyPrecipitations[hour] = baseAmount * randomFactor;
            remainingPrecipitation -= hourlyPrecipitations[hour];
        }

        // 第二次调整：将剩余降水量分配到有降水的时段
        if (remainingPrecipitation > 0) {
            int precipitationHours = 0;
            for (int hour = 0; hour < 24; hour++) {
                if (hourlyPrecipitations[hour] > 0.1) {
                    precipitationHours++;
                }
            }

            if (precipitationHours > 0) {
                double additionalPerHour = remainingPrecipitation / precipitationHours;
                for (int hour = 0; hour < 24; hour++) {
                    if (hourlyPrecipitations[hour] > 0.1) {
                        hourlyPrecipitations[hour] += additionalPerHour;
                    }
                }
            }
        }

        // 确保所有值为非负，并四舍五入到两位小数
        for (int hour = 0; hour < 24; hour++) {
            hourlyPrecipitations[hour] = Math.max(0, Math.round(hourlyPrecipitations[hour] * 100) / 100.0);
        }
    }

    /**
     * 生成更真实的气象数据
     */
    private static WeatherData generateRealisticWeather(int hour, double maxTemp, double minTemp,
                                                        String weatherType, boolean isRainyDay,
                                                        double precipitation, Random random) {

        // 1. 温度变化 - 基于日最高最低温度，遵循日变化规律
        double temperature = calculateHourlyTemperature(hour, maxTemp, minTemp, random);

        // 2. 风向 - 与时间相关
        String windDirection = calculateWindDirection(hour, random);

        // 3. 风速 - 白天较大，晚上较小
        double windSpeed = calculateWindSpeed(hour, weatherType, random);

        // 4. 气压 - 与天气状况相关
        double pressure = calculatePressure(temperature, precipitation, weatherType, random);

        // 5. 云量 - 与天气类型相关
        int cloudCover = calculateCloudCover(weatherType, hour, random);

        // 6. 能见度 - 与天气状况相关
        double visibility = calculateVisibility(precipitation, cloudCover, weatherType, random);

        return new WeatherData(temperature, windDirection, windSpeed,
                precipitation, pressure, cloudCover, visibility);
    }

    /**
     * 计算每小时温度 - 基于日最高最低温度，遵循日变化规律 (1-24时)
     */
    private static double calculateHourlyTemperature(int hour, double maxTemp, double minTemp, Random random) {
        // 温度日变化曲线 - 凌晨最低，午后最高
        double dailyRange = maxTemp - minTemp;

        // 计算每小时相对于最低温度的比例 (1-24时)
        double hourFactor;
        if (hour >= 6 && hour <= 14) {
            // 早晨到午后升温 (6-14时)
            hourFactor = (hour - 6) / 8.0; // 0到1的线性增长
        } else if (hour > 14 && hour <= 18) {
            // 午后高温维持 (15-18时)
            hourFactor = 1.0;
        } else {
            // 晚上降温 (19-24时和1-5时)
            int hoursAfterPeak;
            if (hour > 18) {
                hoursAfterPeak = hour - 18; // 19-24时
            } else {
                hoursAfterPeak = hour + 6;  // 1-5时
            }
            hourFactor = Math.max(0, 1.0 - hoursAfterPeak * 0.1);
        }

        double temperature = minTemp + dailyRange * hourFactor;
        // 添加随机波动 (±1°C)
        temperature += (random.nextDouble() - 0.5) * 2;

        return Math.round(temperature * 10) / 10.0;
    }

    /**
     * 计算风向 (1-24时)
     */
    private static String calculateWindDirection(int hour, Random random) {
        String[] windDirections = {"北", "东北", "东", "东南", "南", "西南", "西", "西北"};

        // 根据时间选择基础风向 (1-24时)
        int baseIndex;
        if (hour >= 9 && hour <= 19) {
            // 白天：偏东南风 (9-19时)
            baseIndex = 3; // 东南
        } else {
            // 晚上：偏西北风 (20-24时和1-8时)
            baseIndex = 7; // 西北
        }

        // 在基础方向上随机偏移
        int variation = random.nextInt(3) - 1; // -1, 0, 1
        int finalIndex = (baseIndex + variation + windDirections.length) % windDirections.length;

        return windDirections[finalIndex];
    }

    /**
     * 计算风速 - 白天较大，晚上较小 (1-24时)
     */
    private static double calculateWindSpeed(int hour, String weatherType, Random random) {
        double baseSpeed;

        // 根据天气类型调整基础风速
        double weatherFactor = 1.0;
        if (weatherType.contains("雨")) {
            weatherFactor = 2.0; // 雨天风速较大
        } else if ("晴".equals(weatherType)) {
            weatherFactor = 1.5; // 晴天风速适中
        }

        if (hour >= 10 && hour <= 17) {
            baseSpeed = random.nextDouble() * 12 + 8; // 白天 8-20 m/s
        } else if (hour >= 7 && hour <= 22) {
            baseSpeed = random.nextDouble() * 6 + 4; // 早晚 4-10 m/s
        } else {
            baseSpeed = random.nextDouble() * 2 + 3; // 夜间 3-5 m/s
        }

        double windSpeed = baseSpeed * weatherFactor;
        return Math.round(windSpeed * 10) / 10.0;
    }

    /**
     * 计算气压 - 与温度、降水相关
     */
    private static double calculatePressure(double temperature, double precipitation, String weatherType, Random random) {
        double basePressure = 1013.25; // 标准大气压 hPa

        // 温度影响：温度高气压略低
        double tempEffect = (temperature - 20) * -0.5;

        // 降水影响：降水时气压较低
        double precipEffect = precipitation > 0 ? -random.nextDouble() * 5 - 2 : 0;

        // 天气类型影响
        double weatherEffect = 0;
        if ("晴".equals(weatherType)) {
            weatherEffect = random.nextDouble() * 2 + 1; // 晴天气压稍高
        } else if (weatherType.contains("雨")) {
            weatherEffect = -random.nextDouble() * 3 - 1; // 雨天气压较低
        }

        double pressure = basePressure + tempEffect + precipEffect + weatherEffect + (random.nextDouble() - 0.5) * 2;

        return Math.round(pressure * 10) / 10.0;
    }

    /**
     * 计算云量 - 与天气类型、时间相关
     */
    private static int calculateCloudCover(String weatherType, int hour, Random random) {
        int baseCloudCover;

        // 根据天气类型确定基础云量
        switch (weatherType) {
            case "晴":
                baseCloudCover = random.nextInt(20) + 10; // 10-30%
                break;
            case "多云":
                baseCloudCover = random.nextInt(40) + 40; // 40-80%
                break;
            case "阴":
                baseCloudCover = random.nextInt(30) + 70; // 70-100%
                break;
            case "小雨":
            case "中雨":
            case "大雨":
            case "暴雨":
                baseCloudCover = random.nextInt(20) + 80; // 80-100%
                break;
            case "雾":
            case "霾":
                baseCloudCover = random.nextInt(30) + 60; // 60-90%
                break;
            default:
                baseCloudCover = random.nextInt(50) + 30; // 30-80%
        }

        // 日变化调整：中午前后云量可能因热对流增加
        if (hour >= 11 && hour <= 15 && !weatherType.contains("雨")) {
            baseCloudCover += random.nextInt(20);
        }

        return Math.min(100, baseCloudCover);
    }

    /**
     * 计算能见度 - 与降水、云量、天气类型相关
     */
    private static double calculateVisibility(double precipitation, int cloudCover, String weatherType, Random random) {
        double visibility;

        if (precipitation > 0) {
            // 降水时能见度较低
            if (precipitation > 8) {
                visibility = random.nextDouble() * 2 + 1; // 大雨：1-3 km
            } else if (precipitation > 2.5) {
                visibility = random.nextDouble() * 3 + 2; // 中雨：2-5 km
            } else {
                visibility = random.nextDouble() * 5 + 3; // 小雨：3-8 km
            }
        } else if ("雾".equals(weatherType) || "霾".equals(weatherType)) {
            // 雾霾天气能见度很低
            visibility = random.nextDouble() * 3 + 1; // 1-4 km
        } else if (cloudCover > 80) {
            // 阴天能见度中等
            visibility = random.nextDouble() * 10 + 5; // 5-15 km
        } else {
            // 晴天能见度较好
            visibility = random.nextDouble() * 15 + 10; // 10-25 km
        }

        return Math.round(visibility * 10) / 10.0;
    }

    /**
     * 气象数据封装类
     */
    static class WeatherData {
        double temperature;
        String windDirection;
        double windSpeed;
        double precipitation;
        double pressure;
        int cloudCover;
        double visibility;

        public WeatherData(double temperature, String windDirection, double windSpeed,
                           double precipitation, double pressure, int cloudCover, double visibility) {
            this.temperature = temperature;
            this.windDirection = windDirection;
            this.windSpeed = windSpeed;
            this.precipitation = precipitation;
            this.pressure = pressure;
            this.cloudCover = cloudCover;
            this.visibility = visibility;
        }
    }
}